#!/usr/bin/python
# Filename: mjc_client.py

import threading
import os
import time
import logging
import socket
import xmltodict
import json

from mjs_db.mjs_db import MjsDB
from mjs_public import mjs_tool

class MjcClient(threading.Thread):

    ip = ""
    user = ""
    user_dir = "/etc/mangjingw/user/"
    report_dir = ""
    # second
    hb_expire = 60

    def __init__(self, socket, ip, server, mjw_server):
        threading.Thread.__init__(self)

        self.sever = server

        self.socket = socket
        self.ip = ip
        self.user = ""

        self.mjw_server = mjw_server

        self.start()
        pass

    def __del__(self):
        self.socket.close()

    def __unregist_user(self):
        if len(self.user) > 0:
            self.sever.unregist(self.user, self.ip)
            self.__update_con_status("offline")

        self.socket.close()

    def run(self):
        max_size = 1024 * 1024
        logging.info("thread recv_thread for client:%s startup" % self.ip)

        # self.socket.settimeout(self.hb_expire)
        # logging.info("setting socket timeout = %d(s)" % self.hb_expire)

        while True:
            try:
                logging.info("ready received mangjingc data")
                req_json = self.socket.recv(max_size)
                if not req_json:
                    logging.info("client:%s disconnected from server" % str(self.ip))
                    self.__unregist_user()
                    break
            except socket.timeout:
                logging.info("client:%s timeout(%ds) disconnected" % (str(self.ip), self.hb_expire))
                self.__unregist_user()
                break
            except Exception as error:
                logging.info("client:%s connected Exception" % str(self.ip))
                self.__unregist_user()
                break

            if len(req_json) == 0:
                logging.error("req_json == 0")
                continue

            logging.debug("request >>> \n%s" % req_json)
            try:
                json_list = req_json.split("}{")
                logging.info("json_list.size() = %d" % len(json_list))
            except Exception, e:
                logging.error("req_json format error : %s" % str(e))
                continue

            if len(json_list) == 1:
                try:
                    req_dict = json.loads(req_json)
                    self.__handle_mjc_req(req_dict)
                except Exception, e:
                    logging.error("json.loads error : %s" % str(e))
                    continue
            else:
                i = 0
                for one_json in json_list:
                    if i == 0:
                        new_json = one_json + "}"
                    elif i == len(json_list) - 1:
                        new_json = "{" + one_json
                    else:
                        new_json = "{" + one_json + "}"

                    logging.info("json_%d >>> \n %s" % (i + 1, new_json))

                    try:
                        req_dict = json.loads(new_json)
                        self.__handle_mjc_req(req_dict)
                    except Exception, e:
                        logging.error("json.loads error : %s" % str(e))

                    i = i + 1

        logging.info("thread recv_thread for client:%s end" % self.ip)
        pass

    def handle_mjw_req(self, req):
        act_name = req['action']
        logging.info("start handle mangjingw action:%s" % act_name)

        if act_name == "examine_all":
            self.__mjw_examine_all(req)
        elif act_name == "start_part_examine":
            pass
        elif act_name == 'user_config_changed' or act_name == 'client_config_changed':    
            self.__mjw_user_config_changed(req)     
        else:
            logging.error("action:%s exist" % act_name)

        logging.info("end handle mangjingw action:%s" % act_name)

    def __handle_mjc_req(self, req):
        msg_type = ''
        if 'type' in req.keys():
            msg_type = req['type']

        act_name = req['action']
        logging.info("start handle mangjingc action:%s" % act_name)

        if (len(self.user) == 0 and act_name != "mjc_auth_req"):
            logging.error("user's server:%s have not auth" % self.ip)
            return

        if msg_type == "im":
            if act_name == "mjc_auth_req":
                self.__mjc_auth_req(req)
            elif act_name == "heartbeat_req":
                self.__mjc_hb_req(req)
            elif act_name == "sysinfo":
                self.__mjc_insert_sys_info(req)
            elif act_name == "cpu_and_mem":
                pass
            elif act_name == "examine_finished":
                self.__mjc_examine_finished(req)
            elif act_name == "examine_item_finished":
                self.__mjc_examine_item_finished(req)
            pass
        elif msg_type == "alarm":
            pass
        else:
            logging.error("action:%s don't exist" % act_name)
            return False
            pass

        logging.info("end handle mangjingc action:%s" % act_name)
        return True
        pass

    def __mjc_auth_req(self, req):
        if 'parameters' not in req.keys():
            logging.error("key:'parameters' not in req")
            return

        parameters = req['parameters']
        user_name = parameters['user_name']
        user_passwd_md5 = parameters['user_passwd_md5']

        # logging.info("username = %s" % user_name)
        # logging.info("user_passwd_md5 = %s" % user_passwd_md5)

        sql = "SELECT COUNT(1) AS num FROM userinfo WHERE phone = '%s' and passwd = '%s'" % (user_name, user_passwd_md5)
        logging.info("sql = %s" % sql)

        db = MjsDB()
        error = ""
        auth_ret = "no"
        results = db.query(sql)

        logging.info("num = %d" % results[0][0])

        if results == None or len(results) == 0 or results[0][0] == 0:
            auth_ret = "no"
            error = "user name or passwd wrong"
        else:
            auth_ret = "yes"
            self.user = user_name
            self.sever.regist(self.user, self.ip)
            self.report_dir = self.user_dir + self.user + "/" + self.ip + "/report/"
            self.__update_con_status("online")

        res_dict = {
            "type": "im",
            "action": "mjc_auth_res",
            "parameters": { "result": auth_ret, "error": error }
        }

        try:
            res_json = json.dumps(res_dict, indent=1)
            logging.debug("response >>> \n%s" % res_json)
            if self.socket.sendall(res_json.encode('utf-8')) != None:
                logging.error("send data to %s failed" % self.ip)
        except Exception, e:
            logging.error(e)

    def __mjc_hb_req(self, req):
        if 'parameters' not in req.keys():
            logging.error("key:'parameters' not in req")
            return

        parameters = req['parameters']
        client_status = parameters['status']

        logging.info("client %s status:%s" % (self.ip, client_status))

        res_dict = {
            "type": "im",
            "action": "heartbeat_res",
            "parameters" : { "expire": self.hb_expire }
        }

        try:
            res_json = json.dumps(res_dict, indent=1)
            logging.debug("response >>> \n%s" % res_json)
            if self.socket.sendall(res_json.encode('utf-8')) != None:
                logging.error("send data to %s failed" % self.ip)
        except Exception, e:
            logging.error(e);

    def __update_con_status(self, status):
        sql = "UPDATE server_info SET con_status = '%s' WHERE user_name = '%s' and address = '%s'" % (status, self.user, self.ip)
        db = MjsDB()
        if not db.execute(sql):
            logging.error("update user = %s, ip = %s, con_status to %s failed" % (self.user, self.ip, status))
            return False

        logging.info("update user = %s, ip = %s, con_status to %s ok" % (self.user, self.ip, status))

        return True

    def __mjc_insert_sys_info(self, req):
        if 'parameters' not in req.keys():
            logging.error("key:'parameters' not in req")
            return False

        parameters = req['parameters']
        sys_name = parameters['sys_name']
        sys_version = parameters['sys_version']
        mjc_version = parameters['mjc_version']

        sql = "SELECT * FROM server_info WHERE user_name = '%s' and address = '%s'" % (self.user, self.ip)
        db = MjsDB()
        results = db.query(sql)
        if results == None or len(results) == 0:
            sql = "INSERT INTO server_info (user_name, address, sys_info, client_ver, con_status) VALUES ('%s', '%s', '%s', '%s', 'online')" % (self.user, self.ip, sys_version, mjc_version)
            if not db.execute(sql):
                return False
            logging.info("update user = %s, ip = %s, con_status to online ok" % (self.user, self.ip))
        else:
            self.__update_con_status("online")

        return True

    def __mjc_examine_finished(self, req):
        res_dict = {
            "action": "examine_finished",
            "user_name": self.user,
            "ip": self.ip,
            "date": req['parameters']['date'],
            "score": req['parameters']['score'],
            "error": req['parameters']['error']
        }

        report_file = self.report_dir + "finished_report.json"

        try:
            res_json = json.dumps(res_dict, indent=1)
            self.__save_file(report_file, res_json)
            self.mjw_server.send(res_json)
        except Exception, e:
            logging.error(e);

    def __mjc_examine_item_finished(self, req):
        res_dict = {
            "action": "examine_item_finished",
            "user_name": self.user,
            "ip": self.ip,
            "module": req['module'],
            "score": req['score'],
            "item": req['item']
        }

        try:
            res_json = json.dumps(res_dict, indent=1)
            self.mjw_server.send(res_json)
        except Exception, e:
            logging.erro(e);

    def __save_file(self, file_path, data):
        logging.info("save file to %s" % file_path)
        try:
            if os.path.exists(file_path):
                os.remove(file_path)

            if not os.path.exists(self.report_dir):
                os.makedirs(self.report_dir)

            report_fo = open(file_path, "wb")
            report_fo.write(data)
            report_fo.close()
        except Exception, e:
            logging.error("error:%s" % str(e))

    def __mjw_examine_all(self, req):
        req_dict = {
            "type": "im",
            "action": "examine_all_req"
        }

        try:
            res_json = json.dumps(req_dict, indent=1)
            logging.debug("send to mangjingc >>> \n%s" % res_json)

            if self.socket.sendall(res_json.encode('utf-8')) != None:
                logging.error("send data to %s failed" % self.ip)
        except Exception, e:
            logging.error("send data to %s failed : %s" % (self.ip, str(e)))

    def __mjw_user_config_changed(self, req):
        req_dict = {
            "type": "im",
            "action": 'sync_config',
            "file_path": req['file_path'],
            "file_name": req['file_name'],
            "file_size": os.path.getsize(req['file_path']),
            "file_md5": mjs_tool.file_md5sum(req['file_path'])
        };

        try:
            res_json = json.dumps(req_dict, indent=1)
            logging.debug("send to mangjingc >>> \n%s" % res_json)

            if self.socket.sendall(config_data.encode('utf-8')) != None:
                logging.error("send data to %s failed" % self.ip)
        except Exception, e:
            logging.error("send data to %s failed : %s" % (self.ip, str(e)))